#pragma once

#include "plato_node.hh"
#include "plato_node_creator.hh"

extern auto add_node_creator(const char *name, plato::PlatoNodeCreator *creator) -> bool;

namespace plato { namespace node {

struct If : public PlatoNodeCreator {
  using PinNameIndexMap = std::unordered_map<std::string, PlatoPinIndex>;
  PinNameIndexMap pin_name_input_index_map_;
  PinNameIndexMap pin_name_output_index_map_;
  PlatoNodeSyncType sync_type_{PlatoNodeSyncType::NONE};
  std::size_t var_mem_size_{0};
  If() {
    pin_name_input_index_map_.emplace("Do",0);
    pin_name_input_index_map_.emplace("Value",1);
    pin_name_output_index_map_.emplace("True",0);
    pin_name_output_index_map_.emplace("False",1);
  }
  virtual ~If() {}
  virtual auto create(DomainPtr domain_ptr, PlatoNodeID id) -> PlatoNodePtr override {
    auto var_sync_type = PlatoVariableSyncType::NONE;
    if (sync_type_ == PlatoNodeSyncType::SERVER_SIDE) {
      var_sync_type = PlatoVariableSyncType::SENDER;
    } else if (sync_type_ == PlatoNodeSyncType::CLIENT_SIDE) {
      var_sync_type = PlatoVariableSyncType::RECEIVER;
    }
    auto node_ptr = new_node(domain_ptr, PlatoNodeID(3), id, sync_type_);
    node_ptr->set_name("If");
    auto pin_Do = new_pin(domain_ptr, PlatoPinType::EXEC, nullptr);
    node_ptr->add_input(pin_Do);
    auto var_Value = domain_ptr->New<Bool>(var_sync_type);
    auto pin_Value = new_pin(domain_ptr, PlatoPinType::VAR, var_Value);
    node_ptr->add_input(pin_Value);
    auto pin_True = new_pin(domain_ptr, PlatoPinType::EXEC, nullptr);
    node_ptr->add_output(pin_True);
    auto pin_False = new_pin(domain_ptr, PlatoPinType::EXEC, nullptr);
    node_ptr->add_output(pin_False);
    return node_ptr;
  }
  virtual auto static_id() -> PlatoNodeID override {
    return PlatoNodeID(3);
  }
  virtual auto get_pin_input_index(const std::string& pin_name) -> PlatoPinIndex override {
    auto it = pin_name_input_index_map_.find(pin_name);
    if (it == pin_name_input_index_map_.end()) {
      return INVALID_PIN_INDEX;
    }
    return it->second;
  }
  virtual auto get_pin_output_index(const std::string& pin_name) -> PlatoPinIndex override {
    auto it = pin_name_output_index_map_.find(pin_name);
    if (it == pin_name_output_index_map_.end()) {
      return INVALID_PIN_INDEX;
    }
    return it->second;
  }
  virtual auto get_node_memory_size() -> std::size_t override {
    var_mem_size_ += sizeof(Bool);
    return var_mem_size_ + get_node_size() + get_pin_size() * (pin_name_input_index_map_.size() + pin_name_output_index_map_.size());
  }
};

static If creator;

static auto res = add_node_creator("If", &creator);

}}

